home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / Java2 / src / javax / swing / LookAndFeel.java < prev    next >
Encoding:
Java Source  |  1999-05-28  |  12.8 KB  |  410 lines  |  [TEXT/CWIE]

  1. /*
  2.  * @(#)LookAndFeel.java    1.18 98/08/26
  3.  *
  4.  * Copyright 1997, 1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package javax.swing;
  16.  
  17. import java.awt.Font;
  18. import java.awt.event.InputEvent;
  19. import java.awt.event.KeyEvent;
  20. import java.awt.Color;
  21. import java.awt.SystemColor;
  22.  
  23. import javax.swing.text.*;
  24. import javax.swing.border.*;
  25. import javax.swing.plaf.*;
  26.  
  27. import java.net.URL;
  28. import java.io.*;
  29.  
  30. import java.util.StringTokenizer;
  31.  
  32.  
  33. /**
  34.  * Completely characterizes a look and feel from the point of view
  35.  * of the pluggable look and feel components.  
  36.  * 
  37.  * @version 1.18 08/26/98
  38.  * @author Tom Ball
  39.  * @author Hans Muller
  40.  */
  41. public abstract class LookAndFeel 
  42. {
  43.  
  44.     /**
  45.      * Convenience method for initializing a component's foreground
  46.      * and background color properties with values from the current
  47.      * defaults table.  The properties are only set if the current
  48.      * value is either null or a UIResource.
  49.      * 
  50.      * @param c the target component for installing default color/font properties
  51.      * @param defaultBgName the key for the default background
  52.      * @param defaultFgName the key for the default foreground
  53.      * 
  54.      * @see #installColorsAndFont
  55.      * @see UIManager#getColor
  56.      */
  57.     public static void installColors(JComponent c,
  58.                      String defaultBgName,
  59.                                      String defaultFgName)
  60.     {
  61.         Color bg = c.getBackground();
  62.     if (bg == null || bg instanceof UIResource) {
  63.         c.setBackground(UIManager.getColor(defaultBgName));
  64.     }
  65.  
  66.         Color fg = c.getForeground();
  67.     if (fg == null || fg instanceof UIResource) {
  68.         c.setForeground(UIManager.getColor(defaultFgName));
  69.     } 
  70.     }
  71.  
  72.  
  73.     /**
  74.      * Convenience method for initializing a components foreground
  75.      * background and font properties with values from the current
  76.      * defaults table.  The properties are only set if the current
  77.      * value is either null or a UIResource.
  78.      * 
  79.      * @param c the target component for installing default color/font properties
  80.      * @param defaultBgName the key for the default background
  81.      * @param defaultFgName the key for the default foreground
  82.      * @param defaultFontName the key for the default font
  83.      * 
  84.      * @see #installColors
  85.      * @see UIManager#getColor
  86.      * @see UIManager#getFont
  87.      */
  88.     public static void installColorsAndFont(JComponent c,
  89.                                          String defaultBgName,
  90.                                          String defaultFgName,
  91.                                          String defaultFontName) {
  92.         Font f = c.getFont();
  93.     if (f == null || f instanceof UIResource) {
  94.         c.setFont(UIManager.getFont(defaultFontName));
  95.     }
  96.  
  97.     installColors(c, defaultBgName, defaultFgName);
  98.     }
  99.  
  100.  
  101.     /**
  102.      * Convenience method for installing a component's default Border 
  103.      * object on the specified component if either the border is 
  104.      * currently null or already an instance of UIResource.
  105.      * @param c the target component for installing default border
  106.      * @param defaultBorderName the key specifying the default border
  107.      */
  108.     public static void installBorder(JComponent c, String defaultBorderName) {
  109.         Border b = c.getBorder();
  110.         if (b == null || b instanceof UIResource) {
  111.             c.setBorder(UIManager.getBorder(defaultBorderName));
  112.         }
  113.     }
  114.  
  115.  
  116.     /**
  117.      * Convenience method for un-installing a component's default 
  118.      * border on the specified component if the border is 
  119.      * currently an instance of UIResource.
  120.      * @param c the target component for uninstalling default border
  121.      */
  122.     public static void uninstallBorder(JComponent c) {
  123.         if (c.getBorder() instanceof UIResource) {
  124.             c.setBorder(null);
  125.         }
  126.     }
  127.  
  128.  
  129.     /*
  130.      * // see parseKeyStroke (private)
  131.      */
  132.     private static class ModifierKeyword {
  133.     final String keyword;
  134.     final int mask;
  135.     ModifierKeyword(String keyword, int mask) {
  136.         this.keyword = keyword;
  137.         this.mask = mask;
  138.     }
  139.     int getModifierMask(String s) {
  140.         return (s.equals(keyword)) ? mask : 0;
  141.     }
  142.     };
  143.  
  144.  
  145.     /*
  146.      * // see parseKeyStroke (private)
  147.      */
  148.     private static ModifierKeyword[] modifierKeywords = {
  149.     new ModifierKeyword("shift", InputEvent.SHIFT_MASK),
  150.     new ModifierKeyword("control", InputEvent.CTRL_MASK),
  151.     new ModifierKeyword("meta", InputEvent.META_MASK),
  152.     new ModifierKeyword("alt", InputEvent.ALT_MASK),
  153.     new ModifierKeyword("button1", InputEvent.BUTTON1_MASK),
  154.     new ModifierKeyword("button2", InputEvent.BUTTON2_MASK),
  155.     new ModifierKeyword("button3", InputEvent.BUTTON3_MASK)
  156.     };
  157.  
  158.  
  159.     /**
  160.      * Parse a string with the following syntax and return an a KeyStroke:
  161.      * <pre>
  162.      *    "<modifiers>* <key>"
  163.      *    modifiers := shift | control | meta | alt | button1 | button2 | button3
  164.      *    key := KeyEvent keycode name, i.e. the name following "VK_".
  165.      * </pre>
  166.      * Here are some examples:
  167.      * <pre>
  168.      *     "INSERT" => new KeyStroke(0, KeyEvent.VK_INSERT);
  169.      *     "control DELETE" => new KeyStroke(InputEvent.CTRL_MASK, KeyEvent.VK_DELETE);
  170.      *     "alt shift X" => new KeyStroke(InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK, KeyEvent.VK_X);
  171.      * </pre>
  172.      */
  173.     private static KeyStroke parseKeyStroke(String s) 
  174.     {
  175.     StringTokenizer st = new StringTokenizer(s);
  176.     String token;
  177.     int mask = 0;
  178.  
  179.     while((token = st.nextToken()) != null) {
  180.         int tokenMask = 0;
  181.  
  182.         /* if token matches a modifier keyword update mask and continue */
  183.  
  184.         for(int i = 0; (tokenMask == 0) && (i < modifierKeywords.length); i++) {
  185.         tokenMask = modifierKeywords[i].getModifierMask(token);
  186.         }
  187.  
  188.         if (tokenMask != 0) {
  189.         mask |= tokenMask;
  190.         continue;
  191.         }
  192.  
  193.         /* otherwise token is the keycode name less the "VK_" prefix */
  194.  
  195.         String keycodeName = "VK_" + token; 
  196.         int keycode;
  197.         try {
  198.         keycode = KeyEvent.class.getField(keycodeName).getInt(KeyEvent.class);
  199.         }
  200.         catch (Exception e) {
  201.         e.printStackTrace();
  202.         throw new Error("Unrecognized keycode name: " + keycodeName);
  203.         }
  204.  
  205.         return KeyStroke.getKeyStroke(keycode, mask);
  206.     }
  207.  
  208.     throw new Error("Can't parse KeyStroke: \"" + s + "\"");
  209.     }
  210.  
  211.  
  212.     /**
  213.      * Convenience method for building lists of KeyBindings.
  214.      * <p>
  215.      * Return an array of KeyBindings, one for each KeyStroke,Action pair
  216.      * in <b>keyBindingList</b>.  A KeyStroke can either be a string in
  217.      * the format specified by the private <code>parseKeyStroke</code> 
  218.      * method or a KeyStroke object.
  219.      * <p>
  220.      * Actions are strings.  Here's an example:
  221.      * <pre>
  222.      *     JTextComponent.KeyBinding[] multilineBindings = makeKeyBindings( new Object[] {
  223.      *          "UP", DefaultEditorKit.upAction,
  224.      *        "DOWN", DefaultEditorKit.downAction,
  225.      *     "PAGE_UP", DefaultEditorKit.pageUpAction,
  226.      *   "PAGE_DOWN", DefaultEditorKit.pageDownAction,
  227.      *       "ENTER", DefaultEditorKit.insertBreakAction,
  228.      *         "TAB", DefaultEditorKit.insertTabAction
  229.      *  });
  230.      * </pre>
  231.      *
  232.      * @param keyBindingList an array of KeyStroke,Action pairs
  233.      * @return an array of KeyBindings
  234.      */
  235.     public static JTextComponent.KeyBinding[] makeKeyBindings(Object[] keyBindingList) 
  236.     {
  237.     JTextComponent.KeyBinding[] rv = new JTextComponent.KeyBinding[keyBindingList.length / 2];
  238.  
  239.     for(int i = 0; i < keyBindingList.length; i += 2) {
  240.         KeyStroke keystroke = (keyBindingList[i] instanceof KeyStroke)
  241.         ? (KeyStroke)keyBindingList[i]
  242.         : parseKeyStroke((String)keyBindingList[i]);
  243.         String action = (String)keyBindingList[i+1];
  244.         rv[i / 2] = new JTextComponent.KeyBinding(keystroke, action);
  245.     }
  246.  
  247.     return rv;
  248.     }
  249.  
  250.  
  251.     /**
  252.      * Utility method that creates a UIDefaults.LazyValue that creates
  253.      * an ImageIcon UIResource for the specified <code>gifFile</code>
  254.      * filename.
  255.      */
  256.     public static Object makeIcon(final Class baseClass, final String gifFile) {
  257.     return new UIDefaults.LazyValue() {
  258.         public Object createValue(UIDefaults table) {
  259.         /* Copy resource into a byte array.  This is
  260.          * necessary because several browsers consider
  261.          * Class.getResource a security risk because it
  262.          * can be used to load additional classes.
  263.          * Class.getResourceAsStream just returns raw
  264.          * bytes, which we can convert to an image.
  265.          */
  266.                 final byte[][] buffer = new byte[1][];
  267.         SwingUtilities.doPrivileged(new Runnable() {
  268.             public void run() {
  269.             try {
  270.                 InputStream resource = 
  271.                 baseClass.getResourceAsStream(gifFile);
  272.                 if (resource == null) {
  273.                 return; 
  274.                 }
  275.                 BufferedInputStream in = 
  276.                 new BufferedInputStream(resource);
  277.                 ByteArrayOutputStream out = 
  278.                 new ByteArrayOutputStream(1024);
  279.                 buffer[0] = new byte[1024];
  280.                 int n;
  281.                 while ((n = in.read(buffer[0])) > 0) {
  282.                 out.write(buffer[0], 0, n);
  283.                 }
  284.                 in.close();
  285.                 out.flush();
  286.                 buffer[0] = out.toByteArray();
  287.             } catch (IOException ioe) {
  288.                 System.err.println(ioe.toString());
  289.                 return;
  290.             }
  291.             }
  292.         });
  293.  
  294.         if (buffer[0] == null) {
  295.             System.err.println(baseClass.getName() + "/" + 
  296.                        gifFile + " not found.");
  297.             return null;
  298.         }
  299.         if (buffer[0].length == 0) {
  300.             System.err.println("warning: " + gifFile + 
  301.                        " is zero-length");
  302.             return null;
  303.         }
  304.  
  305.                 return new IconUIResource(new ImageIcon(buffer[0]));
  306.         }
  307.     };
  308.     }
  309.  
  310.     /**
  311.      * Return a short string that identifies this look and feel, e.g.
  312.      * "CDE/Motif".  This string should be appropriate for a menu item.
  313.      * Distinct look and feels should have different names, e.g. 
  314.      * a subclass of MotifLookAndFeel that changes the way a few components
  315.      * are rendered should be called "CDE/Motif My Way"; something
  316.      * that would be useful to a user trying to select a L&F from a list
  317.      * of names.
  318.      */
  319.     public abstract String getName();
  320.  
  321.  
  322.     /**
  323.      * Return a string that identifies this look and feel.  This string 
  324.      * will be used by applications/services that want to recognize
  325.      * well known look and feel implementations.  Presently
  326.      * the well known names are "Motif", "Windows", "Mac", "Metal".  Note 
  327.      * that a LookAndFeel derived from a well known superclass 
  328.      * that doesn't make any fundamental changes to the look or feel 
  329.      * shouldn't override this method.
  330.      */
  331.     public abstract String getID();
  332.  
  333.  
  334.     /** 
  335.      * Return a one line description of this look and feel implementation, 
  336.      * e.g. "The CDE/Motif Look and Feel".   This string is intended for 
  337.      * the user, e.g. in the title of a window or in a ToolTip message.
  338.      */
  339.     public abstract String getDescription();
  340.  
  341.  
  342.     /**
  343.      * If the underlying platform has a "native" look and feel, and this
  344.      * is an implementation of it, return true.  For example a CDE/Motif
  345.      * look and implementation would return true when the underlying 
  346.      * platform was Solaris.
  347.      */
  348.     public abstract boolean isNativeLookAndFeel();
  349.  
  350.  
  351.     /**
  352.      * Return true if the underlying platform supports and or permits
  353.      * this look and feel.  This method returns false if the look 
  354.      * and feel depends on special resources or legal agreements that
  355.      * aren't defined for the current platform.  
  356.      * 
  357.      * @see UIManager#setLookAndFeel
  358.      */
  359.     public abstract boolean isSupportedLookAndFeel();
  360.  
  361.  
  362.     /**
  363.      * UIManager.setLookAndFeel calls this method before the first
  364.      * call (and typically the only call) to getDefaults().  Subclasses
  365.      * should do any one-time setup they need here, rather than 
  366.      * in a static initializer, because look and feel class objects
  367.      * may be loaded just to discover that isSupportedLookAndFeel()
  368.      * returns false.
  369.      *
  370.      * @see #uninitialize
  371.      * @see UIManager#setLookAndFeel
  372.      */
  373.     public void initialize() {
  374.     }
  375.  
  376.  
  377.     /**
  378.      * UIManager.setLookAndFeel calls this method just before we're
  379.      * replaced by a new default look and feel.   Subclasses may 
  380.      * choose to free up some resources here.
  381.      *
  382.      * @see #initialize
  383.      */
  384.     public void uninitialize() {
  385.     }
  386.  
  387.     /**
  388.      * This method is called once by UIManager.setLookAndFeel to create
  389.      * the look and feel specific defaults table.  Other applications,
  390.      * for example an application builder, may also call this method.
  391.      *
  392.      * @see #initialize
  393.      * @see #uninitialize
  394.      * @see UIManager#setLookAndFeel
  395.      */
  396.     public UIDefaults getDefaults() {
  397.         return null;
  398.     }
  399.  
  400.     /**
  401.      * Returns a string that displays and identifies this
  402.      * object's properties.
  403.      *
  404.      * @return a String representation of this object
  405.      */
  406.     public String toString() {
  407.     return "[" + getDescription() + " - " + getClass().getName() + "]";
  408.     }
  409. }
  410.